/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package extension.room.tienlen;

import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.entities.User;
import extension.game.room.Game;
import extension.game.room.Player;
import extension.game.room.RoomExtension;
import extension.game.zone.Flag;
import extension.room.analyse.CardSet;
import java.util.LinkedList;
import java.util.List;
import log.Debug;

/**
 *
 * @author daz
 */
public abstract class TienlenGame extends Game {

    protected TienlenResponse tienlenResponse;
    protected int lastFight;
    protected boolean isFinishRound;
    protected int cardFight;
    protected static int numberCardFinish = 4;
    protected boolean isChargePenalty;
    protected TienlenPlayer[] listPlayers;
    protected RoomExtension tienlenExtension;
    protected int lastBlock;
    protected int currentRank;
    protected boolean isFinishMatch;
    protected Analyser analyser;
    protected CardSet currentSet;
    protected int currentCard;

    public TienlenGame(RoomExtension tienlenExtension, int roomId) {
        super(tienlenExtension.getParentRoom(), roomId);
        this.tienlenExtension = tienlenExtension;
        numberDealCard = 13;
        listPlayers = new TienlenPlayer[maxPlayer];
        this.tienlenResponse = (TienlenResponse) tienlenExtension.getResponse();
        analyser = new Analyser();
    }

    @Override
    public TienlenPlayer[] getListPlayers() {
        return listPlayers;
    }

    @Override
    public TienlenPlayer addPlayer(String name, long money, User user) {
        TienlenPlayer player = new TienlenPlayer(name, money);
        player.setUser(user);
        if (numberPlayings <= 0 || numberPlayers <= 0) {
            isPlay = false;
        }
        if (numberPlayers < maxPlayer || player.getMoney() < betMoney * 5) {
            int seatPos = -1;
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] == null) {
                    seatPos = i;
                    player.setSeatPos(i);
                    listPlayers[seatPos] = player;
                    if (!isPlay) {
                        numberPlayings++;
                    } else {
                        numberWaiting++;
                        listPlayers[seatPos].setState(TienlenPlayer.NOT_READY);
                    }
                    break;
                }
            }
            if (numberPlayings == 1 && seatPos > -1) {
                listPlayers[seatPos].isHost = true;
                tableHost = seatPos;
            }
            numberPlayers++;
        } else {
            player.setSeatPos(-1);
        }

        if (listPlayers[tableHost] == null) {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null) {
                    listPlayers[i].isHost = true;
                    tableHost = i;
                    break;
                }
            }
        }
        addTableInfo();
        return player;
    }

    @Override
    protected void addTableInfo() {
        List<String> listPlayerNames = new LinkedList<String>();
        List<Integer> listPlayerPositions = new LinkedList<Integer>();
        for (int i = 0; i < listPlayers.length; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getState() != TienlenPlayer.PLAYING) {
                    listPlayerPositions.add(0);
                } else {
                    listPlayerPositions.add(1);
                }
                listPlayerNames.add(listPlayers[i].getName());
            } else {
                listPlayerPositions.add(-1);
                listPlayerNames.add("0");
            }
        }
        room.setProperty(Flag.NUMBER_PLAYER, numberPlayings);
        room.setProperty(Flag.ROOM_PLAYER_NAME, listPlayerNames);
        room.setProperty(Flag.ROOM_PLAYER_POS, listPlayerPositions);
        Room lobbyRoom = tienlenExtension.getParentZone().getRoomByName("The Lobby");
        List<User> listSend = lobbyRoom.getUserList();
        for (User u : listSend) {
            String groupView = (String) u.getProperty(Flag.GROUP_NAME);
            if (groupView.equals(room.getGroupId())) {
                tienlenResponse.sendTableInfo(room.getName(), listPlayerNames, listPlayerPositions, lobbyRoom.getUserList());
            }
        }
    }

    @Override
    public long removePlayer(int leaveId) {
        numberPlayers--;
        long playerMoney = 0;
        if (listPlayers[leaveId] != null) {
            Debug.d(listPlayers[leaveId].getName() + ": state -" + listPlayers[leaveId].getState());
            if ((listPlayers[leaveId].getState() == TienlenPlayer.PLAYING && isPlay)
                    || (!isPlay)) {
                numberPlayings--;
                Debug.d("number playing when remove :" + numberPlayings);

                if (isPlay) {
                    penaltyMoney += betMoney * 5;
                    listPlayers[leaveId].updateMoney(-betMoney * 5);
                    playerMoney = listPlayers[leaveId].getMoney() + listPlayers[leaveId].getTotalMoney();
                    listPlayers[leaveId] = null;
                    if (leaveId == tableHost) {
                        updateTableHost();
                    }
                    if (numberPlayings - currentRank < 2) {
                        onFinishMatch();
                    }
                    if (gameTurn == leaveId) {
                        nextTurn();
                    }


                } else {
                    int numberReadys = 0;
                    playerMoney = listPlayers[leaveId].getMoney() + listPlayers[leaveId].getTotalMoney();
                    listPlayers[leaveId] = null;
                    if (leaveId == tableHost) {
                        updateTableHost();
                    }
                    for (int i = 0; i < maxPlayer; i++) {
                        if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.READY) {
                            numberReadys++;
                        }
                    }
                    if (numberReadys == numberPlayings) {
                        startGame();
                    }
                    if (numberReadys == numberPlayers - 1 && numberPlayers > 1) {
                        setIsHostTimeOut(true);
                        tienlenResponse.sendTimeCount(tableHost);
                    }
                }

            } else {
                if (listPlayers[leaveId].getState() == TienlenPlayer.FINISH) {
                    updateRank(leaveId);
                    if (numberPlayings - currentRank < 2) {
                        onFinishMatch();
                    }
                }
                playerMoney = listPlayers[leaveId].getMoney() + listPlayers[leaveId].getTotalMoney();
                listPlayers[leaveId] = null;
                if (leaveId == tableHost) {
                    updateTableHost();
                }
            }
            if (numberPlayings <= 0) {
                reset();
            }
            addTableInfo();
        }
        return playerMoney;
    }

    @Override
    public void updateTableHost() {
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                tableHost = i;
                listPlayers[i].isHost = true;
                if (isPlay) {
                    tienlenResponse.sendUpdateTableHost(i, 1);
                } else {
                    listPlayers[i].setState(TienlenPlayer.NOT_READY);
                    tienlenResponse.sendUpdateTableHost(i, 0);
                }
                break;
            }
        }
    }

    @Override
    public void run() {
        while (isRunning) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
        }
    }

    @Override
    public int changePlayerState(int readyId) {
        if (listPlayers[readyId] != null) {
            return listPlayers[readyId].changeState();
        }
        return -1;
    }

    private int getCard() {
        int cardId = dealer.nextCard();
        if (cardId == -1) {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null) {
                    tienlenExtension.getApi().kickUser(listPlayers[i].getUser(), null, "", 0);
                    Debug.d("out of dealer index of phom");
                    listPlayers[i] = null;
                }

            }
            onFinishMatch();
        }
        return cardId;
    }

    public boolean getIsFinishRound() {
        return isFinishRound;
    }

    public TienlenPlayer getPlayerFight() {
        return listPlayers[gameTurn];
    }

    public void setCardFight(int cardFight) {
        this.cardFight = cardFight;
    }

    public int getCardFight() {
        return cardFight;
    }

    public int getLastFight() {
        return lastFight;
    }

    public int getNumberCardFinish() {
        return numberCardFinish;
    }

    @Override
    public boolean canHostReady() {
        int numberReadys = 0;
        if (numberPlayings == 1) {
            return false;
        }
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.READY) {
                numberReadys++;
            }
        }
        if (numberReadys == numberPlayings - 1) {
            return true;
        }
        return false;
    }

    @Override
    protected void sendTableInfo(String roomName, List<String> listNames, List<Integer> listPos, List<User> listUser) {
        this.tienlenResponse.sendTableInfo(roomName, listNames, listPos, listUser);
    }

    @Override
    public void sendTimeCount(int host) {
        this.tienlenResponse.sendTimeCount(host);
    }

    @Override
    public void sendUpdateTableHost(int newHost, int isContinue) {
        this.tienlenResponse.sendUpdateTableHost(newHost, isContinue);
    }

    @Override
    protected void sendPlayerKick(String name, int seatPos, User user) {
        this.tienlenResponse.sendPlayerKick(name, seatPos, user);

    }

    @Override
    public int countNumberReady() {
        int result = 0;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.READY) {
                result++;
            }
        }
        return result;
    }

    public List<Integer> getListNumberCard() {
        List<Integer> listResult = new LinkedList<Integer>();
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                listResult.add(listPlayers[i].getNumberCardInHands());
            } else {
                listResult.add(-1);
            }
        }
        return listResult;
    }

    private void reset() {
        if (numberPlayers <= 0) {
            betMoney = betMin;
            winnerId = -1;
        }
        isHostTimeOut = false;
        penaltyMoney = 0;
        isPlay = false;
        lastBlock = -1;
        lastFight = -1;
        currentRank = 0;
        isFinishRound = false;
        isFinishMatch = false;

        numberPlayings = 0;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                listPlayers[i].updateNumberCardInHand();
                listPlayers[i].setState(TienlenPlayer.NOT_READY);
                numberPlayings++;
            }
        }
        for (int i = gameTurn + 1; i < gameTurn + maxPlayer; i++) {
            if (listPlayers[i % maxPlayer] != null) {
                if (listPlayers[i % maxPlayer].getState() == TienlenPlayer.NOT_READY) {
                    gameTurn = i % maxPlayer;
                    break;
                }
            }

        }
    }

    private void updateRank(int leaveId) {
        currentRank--;
        numberPlayings--;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null
                    && listPlayers[i].getRank() > listPlayers[leaveId].getRank()
                    && listPlayers[i].getRank() != 100) {
                listPlayers[i].updateRank(-1);

            }
        }

        List<Integer> listRank = new LinkedList<Integer>();
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                listRank.add(listPlayers[i].getRank());
            } else {
                listRank.add(100);
            }
        }
        tienlenResponse.sendUpdateRank(listRank);
    }

    @Override
    public void startGame() {
        if (!isPlay) {
            int numberReadys = 0;
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.READY) {
                    numberReadys++;
                }
            }

            Debug.d("number ready :" + numberReadys);
            Debug.d("number playing :" + numberPlayings);

            if (numberReadys == numberPlayings && numberPlayings > 1) {
                isPlay = true;
            }
            if (isPlay) {
                isHostTimeOut = false;
                dealCard();
            }
        }
    }

    @Override
    public void dealCard() {
        dealer.shuffleCard();
        lastBlock = -1;
        if (winnerId == -1 || listPlayers[winnerId] == null) {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null) {
                    gameTurn = i;
                    break;
                }

            }
        }
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                listPlayers[i].resetCard();
                if (listPlayers[i].getState() == TienlenPlayer.READY) {
                    for (int j = 0; j < numberDealCard; j++) {
                        int cardId = dealer.nextCard();
                        listPlayers[i].addCard(cardId);
                        if (cardId == 41 && (winnerId == -1 || listPlayers[winnerId] != null)) {
                            gameTurn = i;
                        }
                    }
                    listPlayers[i].updateNumberCardInHand();
                    tienlenResponse.sendDealCard(listPlayers[i].getListCardIds(), listPlayers[i].getUser());
                    listPlayers[i].setState(TienlenPlayer.PLAYING);
                }
            }
        }
        if (winnerId != -1) {
            gameTurn = winnerId;
        }
        Debug.d("game turn :" + gameTurn);
        Debug.d("winner id :" + winnerId);
        tienlenResponse.sendFirstTurn(gameTurn, lastBlock, tienlenExtension.getParentRoom().getUserList());
    }

    private void findTurn() {
        lastFight = gameTurn;
        for (int i = gameTurn + 1; i < maxPlayer + gameTurn; i++) {
            if (listPlayers[i % maxPlayer] != null && listPlayers[i % maxPlayer].getState() == TienlenPlayer.PLAYING) {
                gameTurn = i % maxPlayer;
                break;
            }
        }
    }

    @Override
    public void nextTurn() {
        if (!isFinishMatch) {
            findTurn();
//            Debug.d("last fight :" + lastFight);
//            Debug.d("game turn :" + gameTurn);
//            Debug.d("last block :" + lastBlock);
            if (gameTurn == lastBlock && lastBlock != -1) {
                isFinishRound = true;
            } else if (lastFight == gameTurn && lastBlock != -1) {
                isFinishRound = true;
                newRoundState();
                findTurn();
            }
            if (isFinishRound) {
                onFinishRound();
            }
            sendFightTurn();
        }
    }

    private void newRoundState() {
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getState() == TienlenPlayer.SKIP) {
                    listPlayers[i].setState(TienlenPlayer.PLAYING);
                }
            }
        }
    }

    private void onFinishRound() {
        Debug.d("finish round");
        lastBlock = -1;
        isFinishRound = false;
        newRoundState();
        sendFinishRound();
    }

    private void findLastPlayer() {
        for (int i = 0; i < maxPlayer; i++) {
            {
                if (listPlayers[i] != null) {
                    if ((listPlayers[i].getState() == TienlenPlayer.PLAYING
                            || listPlayers[i].getState() == TienlenPlayer.SKIP)
                            && listPlayers[i].getState() != TienlenPlayer.FINISH) {
                        listPlayers[i].setState(TienlenPlayer.FINISH);
                        tienlenResponse.sendFightAll(currentRank, i, numberPlayers - numberPlayings);
                        listPlayers[i].setRank(currentRank);
                        checkRemainCard(listPlayers[i]);
                        break;
                    }
                }
            }

        }
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.PLAYING) {
                Debug.d(listPlayers[i].getName() + " : rank " + listPlayers[i].getRank());
            }
        }
    }

    protected abstract void checkRemainCard(TienlenPlayer tienlenPlayer);

    private void calculateMoneyChange() {
        winnerId = 0;
        long totalMoneyChange = 0;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                int rank = listPlayers[i].getRank();
                if (rank == 0) {
                    winnerId = i;
                } else {
                    int penaltyTimes = listPlayers[i].getNumberSpcialCardRemains();
                    Debug.d(listPlayers[i].getName() + ":penalty time=" + penaltyTimes);
                    if (penaltyTimes > 0) {
                        totalMoneyChange += betMoney * (rank + 1) * penaltyTimes;
                        listPlayers[i].setMoneyChange(-betMoney * (rank + 1) * penaltyTimes);
                    } else {
                        listPlayers[i].setMoneyChange(-betMoney * rank);
                        totalMoneyChange += betMoney * rank;
                    }
                }
            }
        }
        totalMoneyChange += penaltyMoney;
        if (winnerId != -1 && listPlayers[winnerId] != null) {
            listPlayers[winnerId].setMoneyChange(totalMoneyChange);
        }
    }

    public void onFinishMatch() {
        Debug.d("on finish match");
        //find remain player
        findLastPlayer();
        calculateMoneyChange();

        List<Long> listMoneyResult = new LinkedList<Long>();
        List<Long> listPlayerMoney = new LinkedList<Long>();
        winnerId = -1;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null && listPlayers[i].getState() == TienlenPlayer.FINISH) {
                long moneyChange = listPlayers[i].getMoneyChange();
                listMoneyResult.add(moneyChange);
                listPlayers[i].updateMoneChange();
                listPlayerMoney.add(listPlayers[i].getMoney());
                if (listPlayers[i].getRank() == 0) {
                    winnerId = i;
                }
            } else {
                listMoneyResult.add(-1l);
                listPlayerMoney.add(-1l);
            }
        }

        reset();

        tienlenResponse.sendFinishMatch(listMoneyResult, listPlayerMoney, winnerId, tableHost, numberPlayers);

        updateDatabase();

        checkAllowPlay();

    }

    @Override
    public void updateDatabase() {
        if (!Debug.isDebug) {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null) {
                    tienlenExtension.getDatabase().updateMoneyJoin(tienlenExtension.getRoomPrefix(), listPlayers[i].getName(), listPlayers[i].getMoney());
                }
            }
        }

    }

    @Override
    protected void checkAllowPlay() {
        if (!Debug.isDebug) {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null && listPlayers[i].getMoney() < betMoney * 5) {
                    tienlenResponse.sendPlayerKick(listPlayers[i].getName(), listPlayers[i].getSeatPos(), listPlayers[i].getUser());
                }
            }
        } else {
            for (int i = 0; i < maxPlayer; i++) {
                if (listPlayers[i] != null && listPlayers[i].getMoney() < 0) {
                    tienlenResponse.sendPlayerKick(listPlayers[i].getName(), listPlayers[i].getSeatPos(), listPlayers[i].getUser());
                }
            }
        }
    }

    protected abstract List<Integer> getListBlock();

    private void sendFightTurn() {
        List<Integer> listBlock = getListBlock();
        tienlenResponse.sendGameTurn(gameTurn, lastBlock, listBlock);
    }

    protected abstract void extractSet(int[] cardCheck);

    public void removeCardInHand(List<Integer> listCard) {
        if (!listCard.isEmpty()) {
            if (listCard.size() > 1) {
                int[] cardCheck = new int[listCard.size()];
                int count = 0;
                for (Integer card : listCard) {
                    cardCheck[count] = card;
                    count++;
                }

                extractSet(cardCheck);
                currentCard = -1;
            } else {
                currentCard = listCard.get(0);
                currentSet = null;
            }
            lastBlock = gameTurn;
            listPlayers[gameTurn].removeCardInHand(listCard);
        }
    }

    public void skipTurn(int skipId) {
        if (skipId >= 0 && skipId < maxPlayer && lastBlock != -1) {
            listPlayers[skipId].setState(TienlenPlayer.SKIP);
            tienlenResponse.sendSkipTurn(skipId);
        }
    }

    private void sendFinishRound() {
        tienlenResponse.sendFinishRound();
    }

    public int playerFinish() {
        listPlayers[gameTurn].setRank(currentRank);
        listPlayers[gameTurn].setState(TienlenPlayer.FINISH);
        if (currentRank == 0) {
            winnerId = gameTurn;
        }
        currentRank++;
        if (currentRank == numberPlayings - 1) {
            isFinishMatch = true;

        }
        return (currentRank - 1);
    }

    public boolean getIsFinishMatch() {
        return isFinishMatch;
    }

    public void setReadyState(int i) {
        listPlayers[i].setState(TienlenPlayer.READY);
    }

    public void setIsChargePenalty(boolean isChargePenalty) {
        this.isChargePenalty = isChargePenalty;
    }

    public abstract boolean checkCard(List<Integer> listCards);

    public boolean getIsChargePenalty() {
        return isChargePenalty;
    }

    public int getLastBlock() {
        return lastBlock;
    }

    protected abstract long getMoneyChange();

    public void updateMoneChange() {
        long moneyChange = getMoneyChange();
        listPlayers[gameTurn].updateMoney(moneyChange);
        listPlayers[lastBlock].updateMoney(-moneyChange);
        tienlenResponse.sendMoneyChange(moneyChange, listPlayers[gameTurn].getSeatPos(), listPlayers[gameTurn].getMoney(),
                listPlayers[lastBlock].getSeatPos(), listPlayers[lastBlock].getMoney());
        updateDatabase();

    }

    public int getNumbeView() {
        return (numberPlayers - numberPlayings);
    }

    @Override
    public void updateReadyState() {
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                listPlayers[i].setState(TienlenPlayer.NOT_READY);
                tienlenResponse.sendReadyState(i, TienlenPlayer.NOT_READY, 1);
            }
        }
    }

    @Override
    public void sendTableInfo(List<String> listNames, List<Long> listMoneys, List<Integer> listStates, Player player, User user) {
        if (!isPlay) {
            tienlenResponse.sendTableInfo(listNames, listMoneys, listStates, betMoney,
                    isPlay, tableHost, player.getSeatPos(), player.getTotalMoney(),
                    user);
            if (isHostTimeOut) {
                tienlenResponse.sendDeleteHostTimeOut(tableHost);
                setIsHostTimeOut(false);
            }
        } else {
            if (player.getSeatPos() >= 0) {
                tienlenResponse.sendTableInfo(listNames, listMoneys, listStates, betMoney,
                        isPlay, tableHost, player.getSeatPos(), player.getTotalMoney(), getListNumberCard(),
                        user);
            } else {
                tienlenResponse.sendTableInfo(listNames, listMoneys, listStates, betMoney,
                        isPlay, tableHost, player.getSeatPos(), player.getTotalMoney(), getListNumberCard(),
                        user);
            }
        }
    }

    public abstract boolean checkRightRule(List<Integer> listCards);

    public abstract List<Integer> getAutoFightCard();

    @Override
    public void doLoop() {
    }

    @Override
    public void doReady() {
        setIsHostTimeOut(true);
        tienlenResponse.sendTimeCount(tableHost);
    }

    @Override
    public void updateTableInfo(List<String> listNames, List<Long> listMoneys, List<Integer> listStates, Player player) {
        sendTableInfo(listNames, listMoneys, listStates, player, player.getUser());

        if (numberPlayings >= 1 && player.getSeatPos() >= 0) {
            sendUpdateTable(player.getUser(), player);
            sendUpdate();
        }
    }

    private void sendUpdateTable(User user, Player player) {
        List<User> listSend = room.getUserList();
        listSend.remove(user);
        tienlenResponse.sendUpdatePlayers(user.getName(), player.getMoney(), player.getSeatPos(), listSend);
    }

    private void sendUpdate() {
        String groupName = room.getGroupId();
        Room lobbyRoom = tienlenExtension.getParentZone().getRoomByName("The Lobby");
        List<User> listSend = lobbyRoom.getUserList();
        for (User u : listSend) {
            String groupView = (String) u.getProperty(Flag.GROUP_NAME);
            if (groupView.equals(groupName)) {
                tienlenResponse.sendUpdateTable(room.getName(), numberPlayings, room.getGroupId(), betMoney, u);
            }
        }
    }

    @Override
    public void updateNumberInRoom() {
        numberInRooms = 0;
        for (int i = 0; i < maxPlayer; i++) {
            if (listPlayers[i] != null) {
                numberInRooms++;
            }
        }
    }
}
